package mosdi.fa;

import java.util.Arrays;
import java.util.List;
import java.util.Random;

public class IIDTextModel extends AutomatonBasedTextModel {

	private int alphabetSize;
	private double[] charDist;

	/** Constructs uniformly distributed i.i.d. model. */
	public IIDTextModel(int alphabetSize) {
		this.alphabetSize = alphabetSize;
		charDist = new double[alphabetSize];
		Arrays.fill(charDist, 1.0/(double)alphabetSize);
	}
	
	public IIDTextModel(int alphabetSize, double[] characterDistribution) {
		if (characterDistribution.length != alphabetSize) throw new IllegalArgumentException("Distribution has wrong length.");
		this.alphabetSize = alphabetSize;
		double sum = 0.0;
		for (int i=0; i<characterDistribution.length; ++i) sum+=characterDistribution[i];
		this.charDist = new double[characterDistribution.length];
		for (int i=0; i<characterDistribution.length; ++i) this.charDist[i] = characterDistribution[i]/sum;
	}

	/** Estimate distribution from a given text. */
	public IIDTextModel(Alphabet alphabet, String background) {
		this(alphabet.size(), alphabet.buildIndexArray(background));
	}

	/** Estimate distribution from a given text. */
	public IIDTextModel(int alphabetSize, int[] background) {
		this.alphabetSize = alphabetSize;
		double[] freq = new double[alphabetSize];
		for (int i=0; i<background.length; ++i) {
			freq[background[i]]+=1;
		}
		for (int i=0; i<freq.length; ++i) {
			freq[i]/=(double)background.length;
		}
		charDist = freq;
	}

	/** Estimate distribution from a given list of texts. */
	public IIDTextModel(Alphabet alphabet, List<String> background) {
		this.alphabetSize = alphabet.size();
		double[] freq = new double[alphabetSize];
		long n = 0; 
		for (String s : background) {
			for (int i=0; i<s.length(); ++i) {
				freq[alphabet.getIndex(s.charAt(i))]+=1;
			}
			n+=s.length();
		}
		for (int i=0; i<freq.length; ++i) {
			freq[i]/=(double)n;
		}
		charDist = freq;
	}
	
	/** Estimate distribution from a given list of texts. */
	public IIDTextModel(int alphabetSize, List<int[]> background) {
		this.alphabetSize = alphabetSize;
		double[] freq = new double[alphabetSize];
		long n = 0; 
		for (int[] s : background) {
			for (int c : s) freq[c]+=1;
			n+=s.length;
		}
		for (int i=0; i<freq.length; ++i) {
			freq[i]/=(double)n;
		}
		charDist = freq;
	}

	@Override
	public int[] generateRandomText(int length) {
		int[] s = new int[length];
		Random random = new Random();
		for (int i=0; i<length; ++i) {
			double p = random.nextDouble();
			int c = 0;
			while ((p>charDist[c]) && (c<alphabetSize)) {
				p-=charDist[c];
				c+=1;
			}
			s[i] = c;
		}
		return s;
	}

	public double[] getCharacterDistribution() {
		return charDist;
	}
	
	public int getOrder() { return 0; }


	@Override
	public int[] getContext(int state) {
		return new int[0];
	}

	@Override
	public int getStateCount() {
		return 1;
	}

	@Override
	public double getProbability(int sourceState, int character) {
		if (sourceState!=0) throw new IndexOutOfBoundsException("Illegal state index.");
		if ((character<0) || (character>=alphabetSize)) throw new IndexOutOfBoundsException("Illegal character.");
		return charDist[character];
	}

	@Override
	public int getTransitionTarget(int sourceState, int character) {
		if (sourceState!=0) throw new IndexOutOfBoundsException("Illegal state index.");
		if ((character<0) || (character>=alphabetSize)) throw new IndexOutOfBoundsException("Illegal character.");
		return 0;
	}

	@Override
	public double getEquilibriumProbability(int state) {
		if (state!=0) throw new IndexOutOfBoundsException("Illegal state index.");
		return 1.0;
	}

	@Override
	public int getAlphabetSize() {
		return alphabetSize;
	}
	
}
